package com.dm.service.impl;

import com.alibaba.fastjson.JSON;
import com.dm.constants.Constants;
import com.dm.config.LoadProperties;
import com.dm.dto.UserRegisterDto;
import com.dm.exceptions.BasicException;
import com.dm.exceptions.ExceptionEnum;
import com.dm.interceptors.LoginInterceptor;
import com.dm.mapper.DmImageMapper;
import com.dm.mapper.DmUserMapper;
import com.dm.pojo.DmImage;
import com.dm.pojo.DmUser;
import com.dm.service.DmUserService;
import com.dm.service.MailService;
import com.dm.utils.CommonsCode;
import com.dm.utils.NumberUtil;
import com.dm.utils.TokenUtil;
import com.dm.vo.LoginUserVo;
import com.dm.vo.TokenVo;
import com.dm.vo.UserInfoVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.xml.crypto.Data;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author DongJunJun
 * @Date 2022/9/6 19:32
 */
@Service
public class DmUserServiceImpl implements DmUserService {
    @Resource
    private DmUserMapper dmUserMapper;
    @Resource
    private MailService mailService;
    @Resource
    private RedisTemplate<String,String> redisTemplate;
    @Resource
    private LoadProperties loadProperties;
    @Resource
    private DmImageMapper dmImageMapper;

    /**
     * 发送邮箱
     * @param phone 邮箱/手机
     */
    @Override
    public void sendVerifyCode(String phone) {
        //1.验证邮箱是否有被注册
        //调用本类中的checkEmail可实现该需求
        this.checkEmail(phone);
        //不存在则发送验证码进行注册
        //先根据key获取redis中的values 如果存在则发送获取验证码请求频繁
        String redisKey= Constants.VERIFY_CODE_KEY+phone;
        //获取时也会存在连接异常需要进行捕获
        String verifyCode = null;
        try {
            verifyCode = redisTemplate.opsForValue().get(redisKey);
        } catch (Exception e) {
          //  System.out.println(e.getMessage());
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }

        if (!ObjectUtils.isEmpty(verifyCode)) {
           // System.out.println("verifyCode ERROR");
            throw new BasicException(ExceptionEnum.MAIL_IS_REQUESTS);
        }
        //生成六位随机数
        String code = NumberUtil.generateCode(6);
        //redis未找到key就存入一个对应的code
        ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
        try {
            //有可能redis服务没有开启就会出现异常所以需要try捕获一下
            stringStringValueOperations.set(redisKey,code, Constants.VERIFY_CODE_TIMEOUT, TimeUnit.MINUTES);
        } catch (Exception e) {
           // System.out.println(e.getMessage());
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }
        //获取自定义properties配置文件的邮件模板内容，替换内容中的占位符
        String content = loadProperties.getContent().replace("{code}",code);
        //发送邮件
        mailService.sendSimpleMail(phone,Constants.MAIL_SUBJECT,content);
    }

    /**
     * 判断账号是否已注册
     * @param phone 邮箱/手机
     */
    @Override
    public void checkEmail(String phone) {
        //先判断参数是否为空
        if (StringUtils.isEmpty(phone)) {
            throw new BasicException(ExceptionEnum.MAIL_NONE_ERROR);
        }

        //查询该手机号/邮箱是否已存在,存在则向页面反应相应的信息
        if (!ObjectUtils.isEmpty(dmUserMapper.selectUserByPhone(phone))) {
            throw new BasicException(ExceptionEnum.MAIL_IS_EXISTED);
        }
    }

    /**
     * 账号注册
     * @param userRegisterDto 注册的数据传输对象
     */
    @Override
    public void register(UserRegisterDto userRegisterDto) {
        //1.验证邮箱是否有被注册
        //调用本类中的checkEmail可实现该需求
        this.checkEmail(userRegisterDto.getPhone());

        //2.从redis中判断用户输入的验证码是否正确
        //拼接出redis中的key
        String key = Constants.VERIFY_CODE_KEY+userRegisterDto.getPhone();
        String code = null;
        try {
            code = redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }
        //判断验证码是否已经过期或是不存在
        if (StringUtils.isEmpty(code)) {
            throw new BasicException(ExceptionEnum.VERIFY_CODE_TIMEOUT);
        }
        //判断验证码是否正确
        if (!code.equals(userRegisterDto.getVcode())) {
            throw new BasicException(ExceptionEnum.VERIFY_CODE_ERROR);
        }
        //销毁redis中该用户的验证码
        redisTemplate.delete(key);
        //3.将密码进行加密并且插入一条新的用户数据
        String password = userRegisterDto.getPassword();
        //获取盐值
        String salt = CommonsCode.generateSalt();
        //加密后密码
        String saltHashPwd = CommonsCode.md5Hex(password, salt);
        //构建user对象插入数据库
        DmUser dmUser=new DmUser();
        dmUser.setPhone(userRegisterDto.getPhone());
        dmUser.setPassword(saltHashPwd);
        dmUser.setSalt(salt);
        dmUser.setCreatedTime(new Date());
        //调用持久层接口 录入数据
        if (dmUserMapper.insertUser(dmUser)<0) {
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }
    }

    @Override
    public Object[] login(String phone, String password) {
        //1.根据手机/邮箱号来获取用户信息
        DmUser dmUser = dmUserMapper.selectUserByPhone(phone);
        //判断该用户是否存在
        if (ObjectUtils.isEmpty(dmUser)) {
            throw new BasicException(ExceptionEnum.USER_LOGIN_ERROR);
        }
        //2.存在则,判断密码是否正确
        if (!CommonsCode.md5Hex(password,dmUser.getSalt()).equals(dmUser.getPassword())) {
            throw new BasicException(ExceptionEnum.USER_LOGIN_ERROR);
        }
        //构建loginUserVo
        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setUserId(dmUser.getId());
        BeanUtils.copyProperties(dmUser,loginUserVo);
        //查询用户头像
        List<DmImage> dmImages = this.dmImageMapper.selectImageByCategory(loginUserVo.getUserId(), Constants.USER_IMAGE_CATEGORY_0);
        if (!ObjectUtils.isEmpty(dmImages)) {
            DmImage userHeadPortrait =dmImages.get(0) ;
            loginUserVo.setImageId(userHeadPortrait.getId());
            loginUserVo.setImgUrl(userHeadPortrait.getImgUrl());
        }

        //构建token
        TokenVo tokenVo=new TokenVo(TokenUtil.generateToken(dmUser),Constants.TOKEN_TIMEOUT,System.currentTimeMillis());
        //把token对象存入redis当中
        this.saveToken(tokenVo,loginUserVo);

        return new Object[]{loginUserVo,tokenVo};
    }

    /**
     * 保存token和用户信息
     * @param token
     * @param loginUserVo
     */
    private  void saveToken(TokenVo token,LoginUserVo loginUserVo){
        try {
            if (!StringUtils.isEmpty( redisTemplate.opsForValue().get(token.getToken()))) {
                redisTemplate.delete(token.getToken());
            }
            //后再存入redis中
            redisTemplate.opsForValue().set(token.getToken(),JSON.toJSONString(loginUserVo));
        } catch (Exception e) {
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }
    }

    /**
     * 属性token
     * @param token
     * @return
     */
    @Override
    public TokenVo replaceToken(String token) {
        LoginUserVo loginUserVo = LoginInterceptor.getLoginUserVo();
        try {
            redisTemplate.delete(token);
        } catch (Exception e) {
            throw new BasicException(ExceptionEnum.MAIL_SEND_ERROR);
        }
        DmUser dmUser=new DmUser();
        BeanUtils.copyProperties(loginUserVo,dmUser);
        dmUser.setId(loginUserVo.getUserId());

        String newToken = TokenUtil.generateToken(dmUser);

        //存入新token
        redisTemplate.opsForValue().set(newToken,JSON.toJSONString(loginUserVo));
        return new TokenVo(newToken,Constants.TOKEN_TIMEOUT,System.currentTimeMillis());
    }

    /**
     * 展示个人信息详情
     * @param token
     * @return
     */
    @Override
    public UserInfoVo queryUserInfoByToken(String token) {
        LoginUserVo loginUserVo = LoginInterceptor.getLoginUserVo();
        UserInfoVo userInfoVo = dmUserMapper.selectUserById(loginUserVo.getUserId());
        //查询用户头像
        List<DmImage> dmImages = dmImageMapper.selectImageByCategory(userInfoVo.getId(), Constants.USER_IMAGE_CATEGORY_0);
        if (!ObjectUtils.isEmpty(dmImages)) {
            DmImage userHeadPortrait =dmImages.get(0);
            userInfoVo.setImgUrl(userHeadPortrait.getImgUrl());
        }
        return userInfoVo;
    }


}
